import math

from src.pp.pp_engine import PPEngine
from myapp.pp_py.src.config.config import PPConfig
import h3

def django_pp(start=None, end=None, waypoints=None, constraints=None, priority=None,noentryareas=None):
    # 加载地图
    map = PPConfig.global_map
    # 加装模块
    add_constraints_components(constraints)
    # 设置禁行区
    noentryarea_indexes = get_noentryarea_indexes(noentryareas)
    PPConfig.noentryareas = noentryarea_indexes
    # 执行路径规划（带路网）
    # path_points = PPEngine.pp_by_road(map, start, end)
    # 执行路径规划（不带路网）
    path_points = []
    if len(waypoints) == 1:
        path_points += PPEngine.pp(map, start, waypoints[0])
        path_points += PPEngine.pp(map, waypoints[0], end)
    elif len(waypoints) > 1:
        path_points += PPEngine.pp(map, start, waypoints[0])
        for index in range(len(waypoints) - 1):
            path_points += PPEngine.pp(map, waypoints[index], waypoints[index + 1])
        path_points += PPEngine.pp(map, waypoints[-1], end)
    else:
        path_points += PPEngine.pp(map, start, end)

    # 清空禁行区
    PPConfig.noentryareas = set()
    # 清空模块
    PPEngine.pp_engine_components.clear()

    return path_points

def dbz_pp(qdwz, zkwz, skwz):
    b1b2 = calculate_perpendicular_points(qdwz[1], qdwz[0], zkwz[1], zkwz[0], skwz[1], skwz[0])
    c1c2 = calculate_perpendicular_points_at_c(qdwz[1], qdwz[0], skwz[1], skwz[0])
    b0 = calculate_b0_point(qdwz[1], qdwz[0], zkwz[1], zkwz[0], skwz[1], skwz[0])
    b1lat = b1b2[1]
    b1lon = b1b2[0]
    b2lat = b1b2[3]
    b2lon = b1b2[2]
    c1lat = c1c2[1]
    c1lon = c1c2[0]
    c2lat = c1c2[3]
    c2lon = c1c2[2]

    a = (qdwz[0], qdwz[1])
    b = (zkwz[0], zkwz[1])
    b1 = (b1lat, b1lon)
    b2 = (b2lat, b2lon)
    c = (skwz[0], skwz[1])
    c1 = (c1lat, c1lon)
    c2 = (c2lat, c2lon)
    b0 = (b0[1], b0[0])

    # 多编组规划
    # path1:a-b0-b-c
    # path2:a-b0-b1-c1
    # path3:a-b0-b2-c2
    path1 = django_pp(a,c,[b0,b])
    path2 = django_pp(a,c1,[b0,b1])
    path3 = django_pp(a,c2,[b0,b2])
    return [path1, path2, path3]

def add_constraints_components(constraints):
    """根据约束条件添加相应的组件"""
    if constraints is None:
        return
    if 1 in constraints:
        PPEngine.add_component('modify_cell_with_water')
    if 2 in constraints:
        PPEngine.add_component('modify_cell_with_vegetation')
    if 3 in constraints:
        PPEngine.add_component('modify_cell_with_building')
    if 4 in constraints:
        PPEngine.add_component('modify_cell_with_geology')
    if 5 in constraints:
        PPEngine.add_component('modify_cell_with_soil')
    if 6 in constraints:
        PPEngine.add_component('modify_cell_with_slope')

def get_noentryarea_indexes(noentryareas):
    """根据禁止区域生成索引"""
    if noentryareas is None:
        return set()
    indexes = set()
    for area in noentryareas:
        # 构建GeoJSON格式的多边形
        geojson = {
            'type': 'Polygon',
            'coordinates': [area]
        }
        area_indexes = h3.polyfill(geojson, 13)
        indexes.update(area_indexes)
    return indexes

def calculate_perpendicular_points(a_lon, a_lat, b_lon, b_lat, c_lon, c_lat, distance=10):
    """
    计算在b点处垂直于ac连线的两个点b1和b2
    """
    # 地球半径（米）
    R = 6371000

    # 将经纬度转换为弧度
    a_lon_rad = math.radians(a_lon)
    a_lat_rad = math.radians(a_lat)
    b_lon_rad = math.radians(b_lon)
    b_lat_rad = math.radians(b_lat)
    c_lon_rad = math.radians(c_lon)
    c_lat_rad = math.radians(c_lat)

    # 计算ac连线的方向向量
    delta_lon = c_lon_rad - a_lon_rad
    delta_lat = c_lat_rad - a_lat_rad

    # 考虑纬度对经度距离的影响
    delta_x = delta_lon * math.cos(b_lat_rad) * R
    delta_y = delta_lat * R

    # 计算ac连线的方向角度
    ac_angle = math.atan2(delta_y, delta_x)

    # 计算垂直于ac的方向角度
    perpendicular_angle1 = ac_angle + math.pi / 2
    perpendicular_angle2 = ac_angle - math.pi / 2

    # 计算b1点坐标
    delta_lon1 = (distance * math.cos(perpendicular_angle1)) / (R * math.cos(b_lat_rad))
    delta_lat1 = (distance * math.sin(perpendicular_angle1)) / R
    b1_lon = math.degrees(b_lon_rad + delta_lon1)
    b1_lat = math.degrees(b_lat_rad + delta_lat1)

    # 计算b2点坐标
    delta_lon2 = (distance * math.cos(perpendicular_angle2)) / (R * math.cos(b_lat_rad))
    delta_lat2 = (distance * math.sin(perpendicular_angle2)) / R
    b2_lon = math.degrees(b_lon_rad + delta_lon2)
    b2_lat = math.degrees(b_lat_rad + delta_lat2)

    return (b1_lon, b1_lat, b2_lon, b2_lat)


def calculate_perpendicular_points_at_c(a_lon, a_lat, c_lon, c_lat, distance=20):
    """
    计算在c点处垂直于ac连线的两个点c1和c2

    参数:
    a_lon, a_lat: a点的经度和纬度
    c_lon, c_lat: c点的经度和纬度
    distance: 从c点延伸的距离，单位米，默认20米

    返回:
    (c1_lon, c1_lat, c2_lon, c2_lat): c1和c2点的经纬度坐标
    """
    # 地球半径（米）
    R = 6371000

    # 将经纬度转换为弧度
    a_lon_rad = math.radians(a_lon)
    a_lat_rad = math.radians(a_lat)
    c_lon_rad = math.radians(c_lon)
    c_lat_rad = math.radians(c_lat)

    # 计算ac连线的方向向量
    delta_lon = c_lon_rad - a_lon_rad
    delta_lat = c_lat_rad - a_lat_rad

    # 考虑纬度对经度距离的影响
    delta_x = delta_lon * math.cos(c_lat_rad) * R
    delta_y = delta_lat * R

    # 计算ac连线的方向角度
    ac_angle = math.atan2(delta_y, delta_x)

    # 计算垂直于ac的方向角度
    perpendicular_angle1 = ac_angle + math.pi / 2
    perpendicular_angle2 = ac_angle - math.pi / 2

    # 计算c1点坐标
    delta_lon1 = (distance * math.cos(perpendicular_angle1)) / (R * math.cos(c_lat_rad))
    delta_lat1 = (distance * math.sin(perpendicular_angle1)) / R
    c1_lon = math.degrees(c_lon_rad + delta_lon1)
    c1_lat = math.degrees(c_lat_rad + delta_lat1)

    # 计算c2点坐标
    delta_lon2 = (distance * math.cos(perpendicular_angle2)) / (R * math.cos(c_lat_rad))
    delta_lat2 = (distance * math.sin(perpendicular_angle2)) / R
    c2_lon = math.degrees(c_lon_rad + delta_lon2)
    c2_lat = math.degrees(c_lat_rad + delta_lat2)

    return (c1_lon, c1_lat, c2_lon, c2_lat)


def calculate_b0_point(a_lon, a_lat, b_lon, b_lat, c_lon, c_lat, distance=20):
    """
    计算b0点坐标 - b点沿着AC连线向A点方向移动指定距离
    """
    # 地球半径（米）
    R = 6371000

    # 将经纬度转换为弧度
    a_lon_rad = math.radians(a_lon)
    a_lat_rad = math.radians(a_lat)
    b_lon_rad = math.radians(b_lon)
    b_lat_rad = math.radians(b_lat)
    c_lon_rad = math.radians(c_lon)
    c_lat_rad = math.radians(c_lat)

    # 计算AC连线的方向向量
    delta_lon_ac = c_lon_rad - a_lon_rad
    delta_lat_ac = c_lat_rad - a_lat_rad

    # 考虑纬度对经度距离的影响
    delta_x_ac = delta_lon_ac * math.cos(b_lat_rad) * R
    delta_y_ac = delta_lat_ac * R

    # 计算AC连线的方向角度
    ac_angle = math.atan2(delta_y_ac, delta_x_ac)

    # 计算从B点到A点的方向（与AC方向相反）
    to_a_angle = ac_angle + math.pi

    # 计算B0点坐标
    delta_lon_b0 = (distance * math.cos(to_a_angle)) / (R * math.cos(b_lat_rad))
    delta_lat_b0 = (distance * math.sin(to_a_angle)) / R

    b0_lon = math.degrees(b_lon_rad + delta_lon_b0)
    b0_lat = math.degrees(b_lat_rad + delta_lat_b0)

    return (b0_lon, b0_lat)
